package com.ayu.shortlink.settlement.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.ayu.shortlink.settlement.common.biz.user.UserContext;
import com.ayu.shortlink.settlement.common.enums.TradeStatusEnum;
import com.ayu.shortlink.settlement.common.enums.UserCouponStatusEnum;
import com.ayu.shortlink.settlement.dao.entity.OrderDO;
import com.ayu.shortlink.settlement.dao.entity.UserCouponDO;
import com.ayu.shortlink.settlement.dao.mapper.OrderMapper;
import com.ayu.shortlink.settlement.dao.mapper.UserCouponMapper;
import com.ayu.shortlink.settlement.dto.req.OrderCreateReqDTO;
import com.ayu.shortlink.settlement.dto.req.OrderPageQueryReqDTO;
import com.ayu.shortlink.settlement.dto.resp.OrderCreateRespDTO;
import com.ayu.shortlink.settlement.dto.resp.OrderPageQueryRespDTO;
import com.ayu.shortlink.settlement.dto.resp.OrderQueryRespDTO;
import com.ayu.shortlink.settlement.mq.basis.event.CloseOrderEvent;
import com.ayu.shortlink.settlement.mq.producer.CloseOrderProducer;
import com.ayu.shortlink.settlement.service.OrderService;
import com.ayu.shortlink.settlement.util.GeneratorIdManager;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.google.protobuf.ServiceException;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderDO> implements OrderService {
    private final GeneratorIdManager generatorIdManager;
    private final UserCouponMapper userCouponMapper;
    private final OrderMapper orderMapper;
    private final CloseOrderProducer closeOrderProducer;
    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderCreateRespDTO createOrder(OrderCreateReqDTO requestParam) {
        // 创建订单
        OrderDO orderDO = BeanUtil.toBean(requestParam, OrderDO.class);
        // 设置唯一订单号
        orderDO.setOrderSn(generatorIdManager.generatorID());
        // 设置订单状态为未支付
        orderDO.setStatus(TradeStatusEnum.WAIT_BUYER_PAY.tradeCode());

        // 如果用户使用优惠券 则将用户优惠券进行锁定
        if (requestParam.getUserCouponId() != null){
            LambdaUpdateWrapper<UserCouponDO> updateWrapper = Wrappers.lambdaUpdate(UserCouponDO.class)
                    .eq(UserCouponDO::getUserId, UserContext.getUserId())
                    .eq(UserCouponDO::getId, requestParam.getUserCouponId())
                    .set(UserCouponDO::getStatus, UserCouponStatusEnum.LOCKING.getCode());
            int update = userCouponMapper.update(updateWrapper);
            if (!SqlHelper.retBool(update)){
                throw new ServiceException("优惠券使用失败");
            }
        }
        //进行订单的创建
        orderMapper.insert(orderDO);

        try {
            // 需要考虑定时任务关闭订单(30分钟)
            CloseOrderEvent event = CloseOrderEvent.builder()
                    .userId(Long.parseLong(UserContext.getUserId()))
                    .orderSn(orderDO.getOrderSn())
                    .build();
            SendResult sendResult = closeOrderProducer.sendMessage(event, 30, TimeUnit.MINUTES);
            if (!Objects.equals(sendResult.getSendStatus(), SendStatus.SEND_OK)) {
                throw new ServiceException("投递延迟定时订单消息队列失败");
            }
        }catch (Throwable ex){
            log.error("定时关闭订单消息队列发送错误，请求参数：{}", JSON.toJSONString(requestParam), ex);
            throw ex;
        }
        return BeanUtil.toBean(orderDO, OrderCreateRespDTO.class);
    }

    @Override
    public OrderQueryRespDTO queryByOrderSn(String orderSn) {
        LambdaQueryWrapper<OrderDO> queryWrapper = Wrappers.lambdaQuery(OrderDO.class)
                .eq(OrderDO::getUserId, UserContext.getUserId())
                .eq(OrderDO::getOrderSn, orderSn);
        OrderDO orderDO = orderMapper.selectOne(queryWrapper);
        return BeanUtil.toBean(orderDO, OrderQueryRespDTO.class);
    }

    @Override
    public IPage<OrderPageQueryRespDTO> pageQuery(OrderPageQueryReqDTO requestParam) {
        IPage<OrderDO> page = new Page<>(requestParam.getCurrent(), requestParam.getSize());

        LambdaQueryWrapper<OrderDO> queryWrapper = Wrappers.lambdaQuery(OrderDO.class)
                .eq(OrderDO::getUserId, UserContext.getUserId())
                .eq(requestParam.getStatus() != null, OrderDO::getStatus, requestParam.getStatus());
        IPage<OrderDO> orderPage = orderMapper.selectPage(page, queryWrapper);
        return orderPage.convert(item -> BeanUtil.toBean(item, OrderPageQueryRespDTO.class));
    }

    @SneakyThrows
    @Override
    @Transactional(rollbackFor =Exception.class)
    public void cancelOrder(String orderSn) {
        LambdaQueryWrapper<OrderDO> queryWrapper = Wrappers.lambdaQuery(OrderDO.class)
                .eq(OrderDO::getUserId, UserContext.getUserId())
                .eq(OrderDO::getOrderSn, orderSn);
        OrderDO orderDO = orderMapper.selectOne(queryWrapper);
        if (orderDO == null){
            throw new ServiceException("无法找到订单");
        }
        // 若交易已经结束 则无法进行退款
        if (orderDO.getStatus().equals(TradeStatusEnum.TRADE_FINISHED.tradeCode())){
            throw new ServiceException("交易已经结束，无法取消订单");
        }

        // 需要进行订单取消且优惠券解锁
        LambdaUpdateWrapper<OrderDO> updateWrapper = Wrappers.lambdaUpdate(OrderDO.class)
                .eq(OrderDO::getUserId, UserContext.getUserId())
                .eq(OrderDO::getOrderSn, orderSn)
                .set(OrderDO::getStatus, TradeStatusEnum.TRADE_CANCEL.tradeCode());
        orderMapper.update(updateWrapper);
        // 优惠券解锁(1、如果优惠券还没过期 则设置为UNUSED 2、如果优惠券已经过期 则设置为EXPIRED)
        if (orderDO.getUserCouponId() != null){
            LambdaQueryWrapper<UserCouponDO> lambdaQueryWrapper = Wrappers.lambdaQuery(UserCouponDO.class)
                    .eq(UserCouponDO::getUserId, UserContext.getUserId())
                    .eq(UserCouponDO::getId, orderDO.getUserCouponId());
            UserCouponDO userCouponDO = userCouponMapper.selectOne(lambdaQueryWrapper);
            if (userCouponDO == null){
                throw new ServiceException("无法找到该优惠券");
            }
            Integer nextStatus = isInvalid(userCouponDO)?
                    UserCouponStatusEnum.EXPIRED.getCode() : UserCouponStatusEnum.UNUSED.getCode();
            LambdaUpdateWrapper<UserCouponDO> lambdaUpdateWrapper = Wrappers.lambdaUpdate(UserCouponDO.class)
                    .eq(UserCouponDO::getUserId, UserContext.getUserId())
                    .eq(UserCouponDO::getId, orderDO.getUserCouponId())
                    .set(UserCouponDO::getStatus, nextStatus);
            userCouponMapper.update(lambdaUpdateWrapper);
        }
    }

    /**
     * 检验优惠券是否超过有效期
     * @return true->失效 false->未失效
     */
    private boolean isInvalid(UserCouponDO userCouponDO){
        return new Date().after(userCouponDO.getValidEndTime());
    }
}
